home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <string.h>
- #include <syslog.h>
- #include <errno.h>
- #include <netdb.h>
- #include <unistd.h>
- #include <sys/time.h>
- #include <netinet/in.h>
- #include <netinet/protocols.h>
- #include <limits.h>
- #include "cmdsock.h"
-
- #if 1
- #define DBGALL 1
-
- void logevent(const char *fmt, ...)
- {
- va_list list;
- va_start (list,fmt);
- char buf[300];
- sprintf (buf,fmt,list);
- syslog (LOG_ERR,buf);
- va_end (list);
- }
- void logdebug (int level, const char *fmt, ...)
- {
- va_list list;
- va_start (list,fmt);
- char buf[300];
- int len = sprintf (buf,"Debug level %d : ",level);
- vsprintf (buf+len,fmt,list);
- syslog (LOG_DEBUG,buf);
- va_end (list);
- }
-
- #endif
-
- /*
- Obtient le port de communication via /etc/service
- */
- static int cmdsock_getport(const char *service)
- {
- int ret = -1;
- struct servent *serv = getservbyname (service,"tcp");
- if (serv == NULL){
- logevent ("Pas de service %s dans /etc/service",service);
- }else{
- ret = ntohs(serv->s_port);
- }
- return ret;
- }
-
- PRIVATE void CMDSOCK::init(int port)
- {
- listen_handle = -1;
- if (port != -1){
- /* Il faut creer le socket original pour la connection */
- char myhost[PATH_MAX];
- gethostname (myhost, sizeof(myhost));
- struct hostent *h = (struct hostent *) gethostbyname(myhost);
- struct sockaddr_in sin;
- sin.sin_family = h->h_addrtype;
- logdebug (DBGALL,"Nom du serveur %s %d %d %d.%d.%d.%d\n"
- ,myhost,h->h_addrtype
- ,h->h_length
- ,(unsigned char)h->h_addr[0]
- ,(unsigned char)h->h_addr[1]
- ,(unsigned char)h->h_addr[2]
- ,(unsigned char)h->h_addr[3]);
- memcpy (&sin.sin_addr,h->h_addr, h->h_length);
- sin.sin_port = htons(port);
-
- int i;
- for (i=0; i<5; i++){
- listen_handle = socket (AF_INET, SOCK_STREAM, 0);
- sin.sin_addr.s_addr = INADDR_ANY;
- if (listen_handle == -1){
- logdebug (DBGALL,"listen_handle %d(%s)\n"
- ,errno
- ,strerror(errno));
- }else if (bind (listen_handle,(struct sockaddr *) &sin, sizeof (sin)) == -1){
- logdebug (DBGALL,"bind %d(%s)\n",errno
- ,strerror(errno));
- }else if (::listen (listen_handle,5) == -1){
- logdebug (DBGALL,"listen %d(%s)\n",errno
- ,strerror(errno));
- break;
- }else{
- logdebug (DBGALL,"bind ok\n");
- break;
- }
- close (listen_handle);
- listen_handle = -1;
- if (i < 5) sleep (i*5);
- }
- }
- }
-
- /*
- Ouverture d'un socket en mode listen pour serveur.
- */
- PUBLIC CMDSOCK::CMDSOCK(const char *portname)
- {
- init (cmdsock_getport(portname));
- }
- /*
- Ouverture d'un socket en mode listen pour serveur.
- */
- PUBLIC CMDSOCK::CMDSOCK(int port)
- {
- if (port == -1){
- /* #Specification: CMDSOCK / via inetd
- A server using CMDSOCK can be started with inetd.
- We specify -1 as the port number
- CMDSOCK will then use the handle 0 to wait for
- connections.
- */
- listen_handle = 0;
- }else{
- init (port);
- }
- }
-
- PUBLIC CMDSOCK::~CMDSOCK()
- {
- if (listen_handle != -1){
- SOCK_INFO *pt = inf;
- for (int i=0; i<nbcli; i++,pt++) close (pt->handle);
- close (listen_handle);
- }
- }
- /*
- Retourne != 0 si le serveur est correctement initialise
- */
- PUBLIC int CMDSOCK::is_ok()
- {
- return listen_handle != -1;
- }
- /*
- Ferme la connexion d'un client.
- */
- PUBLIC void CMDSOCK::closecli(int fd)
- {
- close (fd);
- int dst = 0;
- SOCK_INFO *pt = inf;
- for (int i=0; i<nbcli; i++, pt++){
- if (pt->handle != fd){
- inf[dst++] = inf[i];
- }
- }
- nbcli = dst;
- }
-
- /*
- Ajoutte un handle de plus au serveur avec gestion de timeout.
-
- Le timeout est en secondes.
-
- La connexion de ce handle a ΘtΘ realisΘe par l'appelant. On
- retrouve cette situation lorsqu'un serveur dΘmarre lui-mΩme
- les clients ou entre en contact avec un autre serveur.
- */
- PUBLIC void CMDSOCK::addcli (int fd, int timeout)
- {
- SOCK_INFO *pt = inf + nbcli++;
- pt->handle = fd;
- pt->idle.set = timeout * 1000000;
- pt->idle.cur = 0;
- pt->actif = 0;
- }
-
- /*
- Ajoutte un handle de plus au serveur.
- */
- PUBLIC void CMDSOCK::addcli (int fd)
- {
- addcli (fd,0);
- }
-
-
- /*
- Attend de l'activite sur un handle parmi plusieurs ou une connection
- sur un socket.
-
- Return -1 if any error.
- Return 0 if the timeout was met.
- Return > 0 if something is available (or simply a new connexion).
- In that case, readnext() should be called to get some work.
- */
- PUBLIC int CMDSOCK::listen (
- long timeout) // Microseconds
- // -1 = no timeout
- // 0 = pollmode
- {
- fd_set set;
- FD_ZERO(&set);
- int maxhd = 0;
- SOCK_INFO *pt = inf;
- for (int i=0; i<nbcli; i++,pt++){
- int handle = pt->handle;
- pt->actif = 0;
- FD_SET (handle,&set);
- if (handle > maxhd) maxhd = handle;
- }
- FD_SET (listen_handle,&set);
- if (listen_handle > maxhd) maxhd = listen_handle;
- struct timeval timeo;
- if (timeout > 1000000){
- timeo.tv_sec = timeout/1000000;
- timeo.tv_usec = timeout%1000000;
- }else{
- timeo.tv_sec = 0;
- timeo.tv_usec = timeout;
- }
- fd_set spcset;
- spcset = set;
- struct timeval *pttimeo = NULL;
- if (timeout != -1) pttimeo = & timeo;
- int sel = select (maxhd+1,&set,NULL,&spcset,pttimeo);
- actif = 0;
- int ret = 0;
- if (sel > 0){
- if (FD_ISSET(listen_handle,&set) != 0){
- char sacc[100];
- int size=100;
- int fd = accept (listen_handle,(struct sockaddr *)sacc
- ,&size);
- addcli (fd);
- }
- pt = inf;
- for (i=0; i<nbcli; i++,pt++){
- int handle = pt->handle;
- if (FD_ISSET(handle,&spcset)!=0){
- logdebug (DBGALL,"client %d dans spcset\n",handle);
- }
- if (FD_ISSET(handle,&set)){
- pt->actif = 1;
- pt->idle.cur = 0;
- }else{
- pt->idle.cur += timeout;
- }
- }
- ret = 1;
- }else{
- // Aucune connexion active, on augmente les timeout
- pt = inf;
- for (i=0; i<nbcli; i++,pt++){
- pt->idle.cur += timeout;
- }
- }
- return ret;
- }
-
- /*
- Retourne le nombre de connexion courramment monitorΘ.
- Cette fonction est gΘnΘralement utilisΘe pour dΘcider si on doit
- terminer le service
- */
- PUBLIC int CMDSOCK::getnbcli()
- {
- return nbcli;
- }
- /*
- Lit une transaction du prochain client actif.
- Retourne 0 si le client a fermΘ la connexion.
- Retourne -1 s'il n'y en a plus.
- Retourne le nombre de byte placΘ dans buf. fd contiendra
- le handle du client qui sera utilisΘ pour transmettre
- la reponse.
- */
- PUBLIC int CMDSOCK::readnext(void *buf, int size, int &cli)
- {
- int ret = -1;
- SOCK_INFO *pt = inf + actif;
- for ( ; actif < nbcli; actif++, pt++){
- if (pt->actif){
- cli = pt->handle;
- logdebug (DBGALL,"Transaction du client %d\n",cli);
- int nb = read (cli,buf,size);
- if (nb > 0){
- ret = nb;
- actif++;
- }else{
- logdebug (DBGALL,"Client %d a ferme la connexion\n",cli);
- closecli (cli);
- ret = 0;
- }
- break;
- }else if (pt->idle.set > 0){
- if (pt->idle.cur > pt->idle.set){
- cli = pt->handle;
- ret = 0;
- closecli (cli);
- syslog (LOG_INFO,"Stale connexion %d, closing",cli);
- break;
- }
- }
- }
- return ret;
- }
- #if 0
- /*
- Etablie la connexion avec un serveur.
- Retourne -1 si erreur.
- */
- int cmdsock_connect (const char *servname, int port, int nbretry)
- {
- int ret = -1;
- struct hostent *h = (struct hostent *) gethostbyname(servname);
- if (h == NULL){
- logevent ("Pas de serveur \"%s\" dΘfini",servname);
- }else{
- struct sockaddr_in sin;
- sin.sin_family = h->h_addrtype;
- memcpy (&sin.sin_addr,h->h_addr, h->h_length);
- sin.sin_port = htons(port);
- for (int i=0; i<nbretry; i++){
- int s;
- if ((s = socket (AF_INET, SOCK_STREAM, 0)) >= 0) {
- logdebug (DBGALL,"avantconnect %d\n",s);
- if (connect (s, (struct sockaddr *) &sin, sizeof (sin))
- == -1){
- if (i==0){
- logdebug (DBGALL,"Ne peut realiser connect (%s)\n"
- ,strerror(errno));
- }
- close (s);
- sleep(1);
- }else{
- ret = s;
- break;
- }
- }else{
- logdebug (DBGALL,"socket");
- }
- }
- }
- return ret;
- }
- /*
- Etablie la connexion avec un serveur.
- Retourne -1 si erreur.
- */
- int cmdsock_connect (const char *servname, const char *portname, int nbretry)
- {
- int port = cmdsock_getport(portname);
- int ret = -1;
- if (port != -1){
- ret = cmdsock_connect (servname,port,nbretry);
- }
- return ret;
- }
- /*
- Etablie la connexion avec le serveur.
- Retourne -1 si erreur.
- */
- int cmdsock_connect ()
- {
- /* #Specification: serveur / location
- Les clients du serveur assume qu'il execute toujours
- sur ngdhost. Il s'agit donc de mettre le bon alias dans
- /etc/hosts ou ailleurs.
- */
- /* #Specification: serveur / port de communication
- La communication se fait par socket. Le port de communication
- est controlΘ par /etc/service. On y retrouvera la ligne
- suivante
-
- ngd xxx/tcp
-
- xxx sera un numΘro de port non utilisΘ < 1024.
- */
- return cmdsock_connect ("ngdhost","ngd",10);
- }
- /*
- Envoie un seul message via un socket. Le socket est Θtablie
- α chaque transaction.
-
- Retourne -1 si erreur.
- */
- int cmdsock_sendmsg (char *retbuf, int size, const char *ctl, ...)
- {
- int ret = -1;
- int fd = cmdsock_connect();
- if (fd != -1){
- va_list list;
- va_start (list,ctl);
- char buf[1000];
- int nb = vsprintf (buf,ctl,list);
- buf[nb] = '\0';
- write (fd,buf,nb);
- int readnb = read (fd,retbuf,size);
- if (readnb > 0){
- retbuf[readnb] = '\0';
- ret = 0;
- }
- close (fd);
- }
- return ret;
- }
- /*
- Retourne 1 s'il y a quelques chose a lire sur fd.
- Retourne 0 si le timeout est ΘcoulΘ.
- Retourne -1 s'il y a un erreur.
- */
- int cmdsock_wait (int fd, long timeout)
- {
- int tb[1];
- tb[0] = fd;
- return cmdsock_wait (1,tb,tb,timeout);
- }
-
- /*
- Retourne le nombre de handle prΩt pour une lecture (data disponible)
- Retourne 0 si le timeout est ΘcoulΘ.
- Retourne -1 s'il y a un erreur.
- */
- int cmdsock_wait (int nbfds, int fds[],int ready[], long timeout)
- {
- fd_set set;
- FD_ZERO (&set);
- int maxfd = 0;
- int i;
- for (i=0; i<nbfds; i++){
- int fd = fds[i];
- FD_SET (fd,&set);
- if (fd > maxfd) maxfd = fd;
- }
- struct timeval timeo;
- if (timeout > 1000000){
- timeo.tv_sec = timeout/1000000;
- timeo.tv_usec = timeout%1000000;
- }else{
- timeo.tv_sec = 0;
- timeo.tv_usec = timeout;
- }
- int ret = select (maxfd+1,&set,NULL,NULL,timeout == -1 ? NULL : &timeo);
- if (ret > 0){
- ret = 0;
- for (i=0; i<nbfds; i++){
- int fd = fds[i];
- if (FD_ISSET (fd,&set)) ready[ret++] = fd;
- }
- }
- return ret;
- }
- #endif
-
- #ifdef TEST
- static void usage_test()
- {
- fprintf (stderr,
- "x serv ou x client\n"
- "Le serveur retourne chaque ligne que le client envoie\n"
- "avec un petit ajout\n"
- );
- }
-
- int main (int argc, char *argv[])
- {
- if (argc == 2){
- if (strncmp(argv[1],"ser",3)==0){
- CMDSOCK cmd;
- if (cmd.is_ok()){
- while (1){
- if (cmd.listen (3000000) != -1){
- char buf[1000];
- int nb;
- int cli;
- while ((nb=cmd.readnext(buf,sizeof(buf),cli))!=-1){
- if (nb == 0){
- printf ("cli %d a ferme la connexion\n",cli);
- }else{
- char repond[2000];
- buf[nb] = '\0';
- printf ("recoit requete :%s:\n",buf);
- nb = sprintf (repond,"RECU ceci <%s>",buf);
- write (cli,repond,nb);
- }
- }
- }
- }
- }
- }else if (strncmp(argv[1],"cli",3)==0){
- while (1){
- char buf[200];
- printf ("Entrer n'importe quoi "); fflush (stdout);
- fgets(buf,sizeof(buf)-1,stdin);
- char retbuf[200];
- if (cmdsock_sendmsg (retbuf,sizeof(retbuf),"%s",buf)==-1){
- fprintf (stderr,"Ne peut transmettre au serveur\n");
- }else{
- printf ("Recoit :%s:\n",retbuf);
- }
- }
- }else{
- usage_test();
- }
- }else{
- usage_test();
- }
- return 0;
- }
-
-
- #endif
-
-